﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HtmlAgilityPack;
using Framework;
using System.IO;
using System.Web;

namespace SchemaMonitor.html
{
	public class CHtml
	{
		#region Paths
		public static string FolderPath = @"D:\facebook-jeremyconnell7\";
		public static string PhotosPath = FolderPath + "photos\\";
		private static string AlbumPath(long id)
		{
			return string.Concat(PhotosPath, "\\", id, "\\index.htm");
		}
		private static string AlbumDump(long id)
		{
			return string.Concat(PhotosPath, "\\", id, "\\index.bin");
		}

		private static string TimeLinePath { get { return FolderPath + @"html\timeline.htm"; } }
		private static string TimeLineDump { get { return FolderPath + @"html\timeline.bin"; } }

		private static string MessagesPath { get { return FolderPath + @"html\messages.htm"; } }
		private static string MessagesDump { get { return FolderPath + @"html\messages.bin"; } }


		public static List<long> AlbumIds()
		{
			var list = new List<long>();
			foreach (var i in Directory.GetDirectories(PhotosPath))
			{
				var s = Path.GetFileName(i);
				long temp;
				if (long.TryParse(s, out temp))
					list.Add(temp);
			}
			return list;
		}
		#endregion

		#region Public
		public static List<CThread> ExtractMessages() { return ExtractMessages(MessagesPath); }
		public static CPostList		ExtractPosts() { return ExtractPosts(TimeLinePath); }
		public static List<CPhoto>	ExtractPhotos(long id) { return ExtractPhotos(AlbumPath(id)); }
		public static void ExtractPhotosAndDump(long id)
		{
			var photos = ExtractPhotos(AlbumPath(id));
			DumpPhotos(photos, id);
		}
		public static void ExtractMessagesAndDump()
		{
			var threads = ExtractMessages();
			DumpMessages(threads);
		}




		public static List<CThread> LoadMessages()		{ return (List<CThread>)CBinary.DeserialiseFromBytesAndUnzip(File.ReadAllBytes(MessagesDump)); }
		public static CPostList		LoadPosts()			{ return new CPostList((List<CTimeline>)CBinary.DeserialiseFromBytesAndUnzip(File.ReadAllBytes(TimeLineDump))); }
		public static List<CPhoto>	LoadPhotos(long id) { return (List<CPhoto>)CBinary.DeserialiseFromBytesAndUnzip(File.ReadAllBytes(AlbumDump(id))); }

		public static void DumpMessages(List<CThread> messages)		{ File.WriteAllBytes(MessagesDump, CBinary.SerialiseToBytesAndZip(messages)); }
		public static void DumpPosts(CPostList posts)				{ File.WriteAllBytes(TimeLineDump, CBinary.SerialiseToBytesAndZip(posts)); }
		public static void DumpPhotos(List<CPhoto> photos, long id) { File.WriteAllBytes(AlbumDump(id), CBinary.SerialiseToBytesAndZip(photos)); }
		#endregion

		#region Posts
		private static CPostList ExtractPosts(string filePath)
		{
			var doc = new HtmlDocument();
			doc.Load(filePath);
			var root = doc.DocumentNode;

			var list = new CPostList();
			ExtractPosts_Recurse(root, list);
			return list;
		}
		private static void ReportPosts(List<CTimeline> list)
		{
			foreach (var i in list)
			{
				if (null == i.Intro)
				{
					Console.WriteLine("Text: " + i.Comment);
					continue;
				}

				Console.WriteLine(i.Intro ?? string.Empty);
				if (i.IsProfilePic)
					Console.WriteLine("IsProfilePic");
				else if (i.IsShared)
					Console.WriteLine("Shared: " + i.SharedWhat);
				else if (i.IsLike)
					Console.WriteLine("Likes: " + i.LikesWho);
				else if (i.IsJoined)
					Console.WriteLine("Joined: " + i.JoinedWhat);
				else if (i.IsPostedVideoTimeline)
					Console.WriteLine("Posted Video");
				else if (i.IsComment)
					Console.WriteLine("Comment");
				else if (i.IsStatusUpdate)
					Console.WriteLine("Status update");
				else if (i.IsWith)
					Console.WriteLine("With: " + i.WithWho);
				else if (i.IsAddedPhotoToMyTimeline)
					Console.WriteLine("IsAddedPhotoToMyTimeline");
				else if (i.IsChanged)
					Console.WriteLine("Changed: " + i.ChangedWhat);
				else if (i.IsInvitedTo)
					Console.WriteLine("InvitedTo: " + i.InvitedToWhat);
				else if (i.IsRecommends)
					Console.WriteLine("Recommends: " + i.RecommendsWhat);
				else if (i.IsUpdated)
					Console.WriteLine("Updated: " + i.UpdatedWhat);
				else if (i.IsWentTo)
					Console.WriteLine("WentTo: " + i.WentToWhat);
				else if (i.NowFrields)
					Console.WriteLine("NowFrields: " + i.FriendsWith);
				else if (i.WasAddedTo)
					Console.WriteLine("WasAddedTo: " + i.AddedTo);
				else if (i.PhotosAdded > 0)
					Console.WriteLine("Photos: " + i.PhotosAdded.ToString());
				else if (i.PhotosAddedToAlbum > 0)
					Console.WriteLine("Photos: " + i.PhotosAddedToAlbum.ToString());
				else
					Console.WriteLine("??" + i.Intro);

			}

		}

		private static void ExtractPosts_Recurse(HtmlNode e, List<CTimeline> list)
		{
			var x = new CTimeline();

			if (!e.HasChildNodes)
				return;
			if (e.Name == "#text")
				return;

			foreach (HtmlNode i in e.ChildNodes)
			{
				if (IsDivMeta(i))
				{
					if (null != x.Date)
					{
						list.Add(x);
						x = new CTimeline();
					}
					x.Date = i.InnerText;
				}
				else if (IsDivComment(i))
				{
					x.Comment = HttpUtility.HtmlDecode(i.InnerText.Trim());
					list.Add(x);
					x = new CTimeline();
					continue;
				}
				else if (null != x.Date)
					x.Intro = i.InnerText;

				ExtractPosts_Recurse(i, list);
			}

			if (null != x.Date)
				if (null != x.Comment || null != x.Intro)
					list.Add(x);
		}

		#endregion


		#region Photos
		private static List<CPhoto> ExtractPhotos(string filePath)
		{
			if (!File.Exists(filePath))
				return new List<CPhoto>(0);

			var doc = new HtmlDocument();
			doc.Load(filePath);
			var root = doc.DocumentNode;

			var list = new List<CPhoto>();
			ExtractPhotos_Recurse(root, list);
			return list;
		}
		private static void ExtractPhotos_Recurse(HtmlNode node, List<CPhoto> list)
		{
			if (!node.HasChildNodes)
				return;
			if (node.Name == "#text")
				return;

			foreach (var i in node.ChildNodes)
			{
				if (i.Name == "#text")
					continue;
				var temp = i.OuterHtml;


				if (!IsDivBlock(i))
				{
					ExtractPhotos_Recurse(i, list);
					continue;
				}

				list.Add(Photo(i));
			}
		}
		private static CComment Comment(HtmlNode node)
		{
			var c = new CComment();

			foreach (var i in node.ChildNodes)
			{
				var temp = i.OuterHtml;
				if (i.Name == "#text")
				{
					if (null != c.User && string.IsNullOrEmpty(c.Comment))
						c.Comment = HttpUtility.HtmlDecode(i.InnerText.Trim());
					continue;
				}

				if (IsDivMeta(i))
					c.Date = i.InnerText;
				else if (IsSpanUser(i))
					c.User = i.InnerText;
				else
					;
			}

			return c;
		}
		private static CPhoto Photo(HtmlNode node)
		{
			var p = new CPhoto();
			foreach (var j in node.ChildNodes)
			{
				var temp2 = j.OuterHtml;
				if (j.Name == "#text")
					continue;

				if (IsImg(j))
				{
					p.FilePath = ImgSrc(j);
					continue;
				}

				if (j.Name.ToUpper() != "DIV")
					continue;

				foreach (var k in j.ChildNodes)
				{
					var temp3 = k.OuterHtml;
					if (k.Name == "#text")
						continue;

					if (IsDivMeta(k))
					{
						p.Date = k.InnerText;
						continue;
					}

					if (IsDivComment(k))
					{
						if (null == p.Comments)
							p.Comments = new List<CComment>();
						p.Comments.Add(Comment(k));
					}
				}
			}
			return p;
		}
        #endregion


        #region Messages
        public static List<CThread> ExtractMessages(Stream stream)
        {
            var doc = new HtmlDocument();
            doc.Load(stream);
            var root = doc.DocumentNode;

            var list = new List<CThread>();
            ExtractMessages_Recurse(root, list);
            return list;
        }

        public static List<CThread> ExtractMessages(string filePath)
		{
			if (!File.Exists(filePath))
				return new List<CThread>(0);

			var doc = new HtmlDocument();
			doc.Load(filePath);
			var root = doc.DocumentNode;

			var list = new List<CThread>();
			ExtractMessages_Recurse(root, list);
			return list;
		}
		private static void ExtractMessages_Recurse(HtmlNode node, List<CThread> list)
		{
			if (!node.HasChildNodes)
				return;
			if (node.Name == "#text")
				return;

			foreach (var i in node.ChildNodes)
			{
				if (IsDivThread(i))
				{
					list.Add(Thread(i));
					continue;
				}
				ExtractMessages_Recurse(i, list);
			}
		}
		private static CThread Thread(HtmlNode node)
		{
			var t = new CThread();
			t.Messages = new List<CMessage>();
			foreach (var i in node.ChildNodes)
			{
				if (IsDivMessage(i))
				{
					t.Messages.Add(Message(i));
					continue;
				}
				if (i.Name == "#text" && string.IsNullOrEmpty(t.Users))
				{
					t.Users = HttpUtility.HtmlDecode(i.InnerText);
					continue;
				}
			}
			return t;
		}
		private static CMessage Message(HtmlNode node)
		{
			var m = new CMessage();
			var n = node.NextSibling;
			if (n.Name == "#text")
				n = n.NextSibling;
			if (n.Name.ToUpper() == "P")
				m.Message = HttpUtility.HtmlDecode(n.InnerText);
			foreach (var i in node.ChildNodes)
				if (IsDivMessageHeader(i))
				{
					foreach (var j in i.ChildNodes)
						if (IsSpanUser(j))
							m.User = HttpUtility.HtmlDecode(j.InnerText);
						else if (IsSpanMeta(j))
							m.Date = HttpUtility.HtmlDecode(j.InnerText);
					break;
				}
			return m;
		}
		#endregion

		#region Utilities
		private static bool IsImg(HtmlNode e) { return e.Name.ToUpper() == "IMG"; }
		private static string ImgSrc(HtmlNode e)
		{
			if (!e.HasAttributes)
				return string.Empty;
			return e.Attributes["src"].Value;
		}


		private static bool IsDivThread(HtmlNode e) { return IsDivX(e, "thread"); }
		private static bool IsDivMessageHeader(HtmlNode e) { return IsDivX(e, "message_header"); }
		private static bool IsDivMessage(HtmlNode e) { return IsDivX(e, "message"); }
		private static bool IsDivBlock(HtmlNode e) { return IsDivX(e, "block"); }
		private static bool IsDivComment(HtmlNode e) { return IsDivX(e, "comment"); }
		private static bool IsDivMeta(HtmlNode e) { return IsDivX(e, "meta"); }
		private static bool IsSpanMeta(HtmlNode e) { return IsSpanX(e, "meta"); }
		private static bool IsSpanUser(HtmlNode e) { return IsSpanX(e, "user"); }


		private static bool IsDivX(HtmlNode e, string x)
		{
			if (e.Name.ToUpper() == "DIV")
			{
				if (e.HasAttributes)
				{
					var a = e.Attributes["class"];
					return (a.Value == x);
				}
			}
			return false;
		}
		private static bool IsSpanX(HtmlNode e, string x)
		{
			if (e.Name.ToUpper() == "SPAN")
			{
				if (e.HasAttributes)
				{
					var a = e.Attributes["class"];
					return (a.Value == x);
				}
			}
			return false;
		}
		public static DateTime ToDate(string s)
		{
			var d = DateTime.MinValue;

			if (string.IsNullOrEmpty(s))
				return d;

			var bits = CUtilities.SplitOn(s, " at ");
			if (bits.Count < 2)
			{
				DateTime.TryParse(s, out d);
				return d;
			}

			var a = bits[0];
			var b = bits[1];
			var i = b.IndexOf("UTC");
			b = b.Substring(0, i);
			s = string.Concat(a, " ", b).Trim();

			DateTime.TryParse(s, out d);
			return d;
		}
		#endregion
	}
}
